#include <iostream>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    //const char* msg1 = "hello stdout!\n";
    //write(1, msg1, strlen(msg1));

    //const char* msg2 = "hello stderror!\n";
    //write(2, msg2, strlen(msg2));

    //将以上代码进行输出重定向,那么只有第一句,会重定向到文件里面
    //第二句不会
    //如果执行下面这条语句,那么标准错误也能重定向到新的文件里面去
    //./test > log.txt 2>&1
    //这个的意思就是将1描述符的内容,拷贝到2里面去
    //跟dup2(1,2)差不多
//************************************************************

    //我们注意到,我们之前进行文件操作的时候
    //一直都没有关闭文件描述符
    //当我们试着关闭文件描述符1的时候
    //发现下面带'\n'的print语句,可以打印数据到屏幕
    //而没有带'\n'的print语句,不可以打印数据到屏幕
    
    //上面这个问题,需要说明一些原理:
    //我们实际上时候两个缓冲区的存在
    //一个是用户级缓冲区,也就是c语言为我们提供的
    //这个缓冲区被封装在FILE 这个结构体中
    //也就是说FILE这个结构体里面,既封装了fd文件描述符
    //又封装了维护c语言缓冲区相关的内容
    //同时我们还有一个系统级缓冲区
    //它是存在于你打开的文件的内核缓冲区
    //当你调用c语言的接口时,我们会把内容写入到c语言的缓冲区
    //然后通过fd向特定的文件的内核缓冲区刷新数据
    //最后进程退出的时候,会刷新FILE内部的数据到文件缓冲区中
    //最后,再从文件缓冲区刷新到对应的文件中去
    
    //同时还要介绍一下缓冲区刷新策略,对于用户层和系统层面都是适用的
    //1.立即刷新(fflush(),不缓冲)
    //2.行刷新(行缓冲'\n'),比如显示器文件就是行缓冲
    //3.缓冲区满了,才刷新(全缓冲),比如一般的文本文件就是全缓冲
    
    //所以导致下面的这种情况的具体原因如下:
    //有'\n'的print,是行刷新,所以遇见'\n'的时候,就把内容从c语言的缓冲区
    //刷新到文件的缓冲区了
    //而没有'\n'的print,就是一直保存在c语言的缓冲区里面
    //最后,我们将文件描述符为1的文件关闭了
    //进程退出的时候,将c语言的缓冲区的内容,刷新到fd为1对应的文件
    //那么print里面调用系统接口时,没有fd对应的文件
    //自然也是没有办法进行刷新数据
    
    int fd = open("./log.txt", O_CREAT | O_RDWR, 0644);

    dup2(fd, 1);

    printf("hello printf!");
    fprintf(stdout, "hello fprintf!");

    printf("hello printf!");
    fprintf(stdout, "hello fprintf!");


    close(1);

    return 0;
}

//int main()
//{
//    close(1);
//
//    //因为对于这些普通的文本文件,c语言的刷新策略是全缓冲
//    //而不是显示器的行缓冲,所以除非c语言的缓冲区满了
//    //或者是自己调用刷新函数,不然会一直在c语言缓冲区里面
//    //最后,又关闭了fd对应的文件,那么自然就不会将内容刷新到
//    //对应的文件缓冲区
//    int fd = open("./log.txt", O_WRONLY | O_CREAT, 0664);
//    printf("fd:%d", fd);
//
//    fprintf(stdout, "hello fprintf!\n");
//    fprintf(stdout, "hello fprintf!\n");
//    fprintf(stdout, "hello fprintf!\n");
//    fprintf(stdout, "hello fprintf!\n");
//    fprintf(stdout, "hello fprintf!\n");
//
//    close(fd);
//    return 0;
//}

//int main()
//{
//    //系统接口
//    //父进程直接将数据写入文件缓冲区
//    const char* msg1 = "hello stdout\n";
//    write(1, msg1, strlen(msg1));
//
//    //c语言的接口
//    //子进程创建的时候,和父进程的代码和数据共享
//    //而缓冲区也是父进程的数据,所以子进程也有这个缓冲区
//    //当最后,进程退出的时候,无论是父进程,还是子进程
//    //都会把c语言缓冲区里面的数据刷新到文件的缓冲区里面
//    //所以发生写时拷贝,代码共享,数据父子各自私有一份
//    //所以最后下面的print后输出两次
//    //注意上面的解释是针对于重定向后的
//    //如果是显示器,那么行缓冲,已经把内容刷新到文件里面了
//    printf("hello printf\n");
//    fprintf(stdout, "hello fprintf\n");
//    fputs("hello fputs\n", stdout);
//
//    fork();
//
//    return 0;
//}
